package com.szeldon.declatest.reports.plugins;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Properties;
import java.util.logging.Logger;

import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Nodes;
import nu.xom.ParsingException;
import nu.xom.ValidityException;
import nu.xom.xslt.XSLException;
import nu.xom.xslt.XSLTransform;

import com.szeldon.declatest.declaration.TestsSuiteDeclaration;
import com.szeldon.declatest.exec.TestSuiteExecHistory;
import com.szeldon.declatest.reports.exceptions.GeneratingReportException;
import com.szeldon.utils.ValuesUtils;

/**
 * Plugin for generating reports in HTML format.
 * 
 * @author szeldon
 * 
 */
public class HtmlReportGeneratorPlugin implements IReportGeneratorPlugin {

    public static final String HTML_REPORTS_SUBDIRECTORY = "html";
    public static final String HTML_REPORTS_XSLT_FILENAME = "html-report.xsl";
    public static final String HTML_REPORTS_XSLT_DIRECTORY = "res";

    private static Logger logger = Logger.getLogger(HtmlReportGeneratorPlugin.class.getName());

    @Override
    public void generateReport(TestsSuiteDeclaration suite, TestSuiteExecHistory history, Properties properties,
            String reportDirectory, String xmlReportFile) throws GeneratingReportException {

        logger.info("Generating report in HTML format for suite " + suite.getId() + " and basing on report data from "
                + xmlReportFile + " file. Output will get into " + reportDirectory + " directory.");

        Document reportDataDocument = filenameIntoDocument(xmlReportFile);
        Document xsltDocument = filenameIntoDocument(HTML_REPORTS_XSLT_DIRECTORY + System.getProperty("file.separator")
                + HTML_REPORTS_XSLT_FILENAME);

        logger.fine("Transforming report data.");
        Document transformedDocument = transform(xsltDocument, reportDataDocument);

        String htmlReportFilename = reportDirectory + System.getProperty("file.separator") + getReportFilename();
        logger.fine("Writing transformed document to file " + htmlReportFilename + ".");
        writeDocumentToFile(transformedDocument, htmlReportFilename);

        logger.info("Report in HTML format is now generated.");
    }

    /**
     * 
     * @param document
     * @param filename
     * @throws GeneratingReportException
     */
    private void writeDocumentToFile(Document document, String filename) throws GeneratingReportException {
        try {
            File file = new File(filename);
            if (!file.exists()) {
                new File(file.getParent()).mkdirs();
                file.createNewFile();
            }
            BufferedWriter writer = new BufferedWriter(new FileWriter(file));
            writer.write(document.toXML());
            writer.close();
        } catch (IOException e) {
            logger.severe("Can't write HTML report to file " + filename + ". " + e.getMessage());
            throw new GeneratingReportException("Can't write HTML report to file " + filename + ". ", e);
        }
    }

    private String getReportFilename() {
        Calendar calendar = GregorianCalendar.getInstance();
        String year = "" + calendar.get(Calendar.YEAR);
        String month = ValuesUtils.convertToTwoDigitString(calendar.get(Calendar.MONTH));
        String day = ValuesUtils.convertToTwoDigitString(calendar.get(Calendar.DAY_OF_MONTH));
        String hour = ValuesUtils.convertToTwoDigitString(calendar.get(Calendar.HOUR_OF_DAY));
        String minute = ValuesUtils.convertToTwoDigitString(calendar.get(Calendar.MINUTE));
        String second = ValuesUtils.convertToTwoDigitString(calendar.get(Calendar.SECOND));
        return "report-" + year + month + day + "-" + hour + minute + second + ".html";
    }

    /**
     * 
     * @param xsltDocument
     * @param reportDataDocument
     * @throws GeneratingReportException
     */
    private Document transform(Document xsltDocument, Document reportDataDocument) throws GeneratingReportException {
        try {
            XSLTransform xslt = new XSLTransform(xsltDocument);
            Nodes transformed = xslt.transform(reportDataDocument);
            return XSLTransform.toDocument(transformed);
        } catch (XSLException e) {
            logger.severe("Error while working with XSLT. " + e.getMessage());
            throw new GeneratingReportException("Error while working with XSLT. " + e.getMessage(), e);
        }
    }

    /**
     * 
     * @param filename
     * @return
     * @throws GeneratingReportException
     */
    private Document filenameIntoDocument(String filename) throws GeneratingReportException {
        Document document = null;

        try {
            document = new Builder().build(new File(filename));
        } catch (ValidityException e) {
            logger.severe("Validity exception while reading XML file (" + filename + ")." + e.getMessage());
            throw new GeneratingReportException("Validity exception while reading XML file (" + filename + ")."
                    + e.getMessage(), e);
        } catch (ParsingException e) {
            logger.severe("Parsing exception while reading XML file (" + filename + ")." + e.getMessage());
            throw new GeneratingReportException("Parsing exception while reading XML file (" + filename + ")."
                    + e.getMessage(), e);
        } catch (IOException e) {
            logger.severe("IO error while reading XML file (" + filename + ")." + e.getMessage());
            throw new GeneratingReportException("IO error while reading XML file (" + filename + ")." + e.getMessage(),
                    e);
        }

        return document;
    }

    @Override
    public String getSubdirectoryName() {
        return HTML_REPORTS_SUBDIRECTORY;
    }
}
